/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package bd.org.apache.commons.math.util;

import bd.org.apache.commons.math.exception.MaxCountExceededException;

import java.util.Collection;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * This abstract class provides a general framework for managing iterative
 * algorithms. The maximum number of iterations can be set, and methods are
 * provided to monitor the current iteration count. A lightweight event
 * framework is also provided.
 *
 * @version $Id: IterationManager.java 1244107 2012-02-14 16:17:55Z erans $
 */
public class IterationManager {

    /**
     * Keeps a count of the number of iterations.
     */
    private final Incrementor iterations;

    /**
     * The collection of all listeners attached to this iterative algorithm.
     */
    private final Collection<IterationListener> listeners;

    /**
     * Creates a new instance of this class.
     *
     * @param maxIterations Maximum number of iterations.
     */
    public IterationManager(final int maxIterations) {

        this.iterations = new Incrementor();

        this.iterations.setMaximalCount(maxIterations);

        this.listeners = new CopyOnWriteArrayList<IterationListener>();
    }

    /**
     * Attaches a listener to this manager.
     *
     * @param listener A {@code IterationListener} object.
     */
    public void addIterationListener(final IterationListener listener) {

        listeners.add(listener);
    }


    /**
     * Informs all registered listeners that the initial phase (prior to the
     * main iteration loop) has been completed.
     *
     * @param e The {@link IterationEvent} object.
     */
    public void fireInitializationEvent(final IterationEvent e) {

        for (IterationListener l : listeners) {

            l.initializationPerformed(e);
        }
    }


    /**
     * Informs all registered listeners that a new iteration (in the main
     * iteration loop) has been performed.
     *
     * @param e The {@link IterationEvent} object.
     */
    public void fireIterationPerformedEvent(final IterationEvent e) {

        for (IterationListener l : listeners) {

            l.iterationPerformed(e);
        }
    }


    /**
     * Informs all registered listeners that a new iteration (in the main
     * iteration loop) has been started.
     *
     * @param e The {@link IterationEvent} object.
     */
    public void fireIterationStartedEvent(final IterationEvent e) {

        for (IterationListener l : listeners) {

            l.iterationStarted(e);
        }
    }


    /**
     * Informs all registered listeners that the final phase (post-iterations)
     * has been completed.
     *
     * @param e The {@link IterationEvent} object.
     */
    public void fireTerminationEvent(final IterationEvent e) {

        for (IterationListener l : listeners) {

            l.terminationPerformed(e);
        }
    }


    /**
     * Returns the number of iterations of this solver, 0 if no iterations has
     * been performed yet.
     *
     * @return the number of iterations.
     */
    public int getIterations() {

        return iterations.getCount();
    }


    /**
     * Returns the maximum number of iterations.
     *
     * @return the maximum number of iterations.
     */
    public int getMaxIterations() {

        return iterations.getMaximalCount();
    }


    /**
     * Increments the iteration count by one, and throws an exception if the
     * maximum number of iterations is reached. This method should be called at
     * the beginning of a new iteration.
     *
     * @throws MaxCountExceededException if the maximum number of iterations is
     * reached.
     */
    public void incrementIterationCount() throws MaxCountExceededException {

        iterations.incrementCount();
    }


    /**
     * Removes the specified iteration listener from the list of listeners
     * currently attached to {@code this} object. Attempting to remove a
     * listener which was <em>not</em> previously registered does not cause any
     * error.
     *
     * @param listener The {@link IterationListener} to be removed.
     */
    public void removeIterationListener(final IterationListener listener) {

        listeners.remove(listener);
    }


    /**
     * Sets the iteration count to 0. This method must be called during the
     * initial phase.
     */
    public void resetIterationCount() {

        iterations.resetCount();
    }
}
